#ifndef _NE_MIDI_SEQUENCE_H_
#define _NE_MIDI_SEQUENCE_H_

#include <audio/MIDI/MIDITrack.h>
#include <system/Resource.h>

namespace ne
{
    class NEAPI MIDISequence : public Resource
    {
        public:
            MIDISequence(ResourceManager *pManager,
                         const uint64_t id,
                         const std::string &name,
                         const std::string &fileName,
                         const bool isManual,
                         ManualResourceLoader *pLoader);

            void setBPM(const uint32_t BPM);
            uint32_t getBPM() const;
            void setMPQN(const uint32_t MPQN);
            uint32_t getMPQN() const;
            void setTPQN(const uint32_t TPQN);
            uint32_t getTPQN() const;

            double time2Ticks(const double time) const;
            double ticks2Time(const double ticks) const;

            // encode / decode
            void decodeEvent(const byte *buffer, uint32_t &offset);
            MIDIEvent* decodeChannelEvent(const byte event, const byte *buffer, uint32_t &offset);
            void decodeMetaEvent(const byte event, const byte *buffer, uint32_t &offset);
            void decodeSysExEvent(const byte event, const byte *buffer, uint32_t &offset);
            uint32_t decodeVariableLengthValue(const byte *buffer, uint32_t &offset);
            std::string decodeString(const byte *buffer, uint32_t &offset, const uint32_t length);

            size_t getNumTracks() const;

            MIDITrack* getTrack(const size_t index) const;

            void destroyAllTracks();

            virtual ~MIDISequence();

        protected:
            virtual bool loadImplement();

            virtual bool unloadImplement();

            virtual bool downloadImplement();

            virtual size_t _calculateSize() const;

        private:
            uint32_t mBPM;
            uint32_t mMPQN;
            uint32_t mTPQN;
            uint32_t mFPS;
            uint32_t mTPF;
            MIDITrackVector mTrackVector;

            byte mLastEventType;
            MIDITrack *mpLastTrack;
    };

    class NEAPI MIDISequencePtr : public SharedPtr<MIDISequence>
    {
        public:
            MIDISequencePtr() : SharedPtr<MIDISequence>() {}
            explicit MIDISequencePtr(MIDISequence *pMIDISequence) : SharedPtr<MIDISequence>(pMIDISequence) {}
            MIDISequencePtr(const MIDISequencePtr &pMIDISequence) : SharedPtr<MIDISequence>(pMIDISequence) {}

            MIDISequencePtr(const ResourcePtr &p)
                : SharedPtr<MIDISequence>()
            {
                mpPtr = static_cast<MIDISequence*>(p.ptr());
                mpUseCount = p.getUseCountPtr();
                if (mpUseCount)
                {
                    ++(*mpUseCount);
                }
            }

            MIDISequencePtr& operator = (const ResourcePtr &p)
            {
                if (mpPtr == static_cast<MIDISequence*>(p.ptr()))
                {
                    return *this;
                }
                MIDISequencePtr temp(p);
                swap(temp);
                return *this;
            }
    };
}

#endif
